<?php
namespace App\Services;

use App\Models\SysFile;
use Illuminate\Http\FileHelpers;

/**
 * 系统文件服务类
 * @package App\Services
 */
class SysFileService
{
    use FileHelpers;
    const TYPE_IMAGE = 'image';
    const TYPE_VIDEO = 'video';
    const TYPE_VOICE = 'voice';
    const TYPE_FILE = 'file';

    private $typeList;
    //文件类别扩展列表
    private $fileTypeExtList = [];

    /**
     * 构造函数
     */
    public function __construct()
    {
        $this->typeList = [
            self::TYPE_IMAGE => '图片',
            self::TYPE_VIDEO => '视频',
            self::TYPE_VOICE => '音频',
            self::TYPE_FILE => '文件',
        ];
        $this->fileTypeExtList = [
            self::TYPE_IMAGE => ["png", "jpg", "jpeg", "gif", "bmp"],
            self::TYPE_VIDEO => ["flv", "swf", "mkv", "avi", "rm", "rmvb", "mpeg", "mpg", "ogg", "ogv", "mov", "wmv", "mp4", "webm"],
            self::TYPE_VOICE => ['mp3', 'wma', 'wav', 'amr', 'mid'],
            self::TYPE_FILE => ["rar", "zip", "tar", "gz", "7z", "bz2", "cab", "iso", "doc", "docx", "xls", "xlsx", "ppt", "pptx", "pdf", "txt", "md", "xml"],
        ];
    }

    /**
     * 获取文件类别列表
     * @return array
     */
    public function getTypeList()
    {
        return $this->typeList;
    }
    /**
     * 保存文件信息到数据库
     * @param $fileData
     * @return mixed
     * @throws \Exception
     */
    public function saveFileInfo($fileData)
    {
        if (empty($fileData['disk_name'])) {
            throw new \Exception('请选择上传文件类型');
        }
        if (empty($fileData['file_type'])) {
            throw new \Exception('请选择文件类型');
        }
        if (empty($fileData['file_path'])) {
            throw new \Exception('请输入文件路径');
        }
        if (empty($fileData['view_url'])) {
            throw new \Exception('请输入文件访问网址');
        }
        if (empty($fileData['new_name'])) {
            throw new \Exception('请输入新的文件名');
        }
        if (empty($fileData['file_ext'])) {
            throw new \Exception('请输入文件后缀');
        }
        return SysFile::create($fileData);
    }

    /**
     * 通过文件扩展名获取文件类别
     * @param $ext
     * @return int|string
     */
    public function getFileTypeByExit($ext)
    {
        foreach ($this->fileTypeExtList as $type => $extList) {
            if (in_array($ext, $extList)) {
                return $type;
            }
        }
        return self::TYPE_FILE;
    }

    /**
     * 通过文件地址上传
     * @param $filePath
     * @return mixed
     * @throws \GuzzleHttp\Exception\GuzzleException
     */
    public function uploadFile($filePath, $fileName = '')
    {
        $client = new \GuzzleHttp\Client;
        $response = $client->request('POST', config('url.upload_file'), [
            'multipart' => [
                [
                    'name'     => 'file',
                    'contents' => fopen($filePath, 'r'),
                    'filename' => $fileName ?? 'image.jpg',
                ],
            ]
        ]);
        $code = $response->getStatusCode(); // 200
        $reason = $response->getReasonPhrase(); // OK
        if ($code != 200) {
            throw new \Exception('上传出错:'. $reason);
        }
        $body = $response->getBody()->getContents();
        $res = json_decode($body, true);
        if (!isset($res[0])) {
            throw new \Exception('出错', 50006);
        }
        return $res[0];
    }

    /**
     * 上传base64文件
     * @param $filePath
     * @return mixed
     * @throws \GuzzleHttp\Exception\GuzzleException
     */
    public function uploadBase64($filePath)
    {
        $client = new \GuzzleHttp\Client([
            'base_uri' => config('url.upload_file_base64'),
            'timeout' => 5,
        ]);
        $response = $client->request('POST', config('url.upload_file_base64'),[
            'block' => 'web',
            'pic' =>  base64_encode(file_get_contents($filePath)),
        ]);
        $code = $response->getStatusCode(); // 200
        $reason = $response->getReasonPhrase(); // OK
        if ($code != 200) {
            throw new \Exception('上传出错:'. $reason);
        }
        $body = $response->getBody();
        $res = json_decode($body, true);
        if (!isset($res['url'])) {
            throw new \Exception('出错', 50006);
        }
        return $res;
    }
    /**
     * 获取远程文件内容并保存到本地
     * @param $remoteUrl
     * @param string $name
     * @return bool
     * @throws \Exception
     */
    public function saveRemoteFile($remoteUrl, $name = '', $defaultExt = 'jpg')
    {
        $ext = pathinfo($remoteUrl, PATHINFO_EXTENSION);
        $ext || $ext = $defaultExt;
        $name || $name = date("YmdHis") . uniqid() . '.' . $ext;
        $dir = 'upload' . DIRECTORY_SEPARATOR . date('Ymd');
        try {
            $client = new \GuzzleHttp\Client();
            $request = new \GuzzleHttp\Psr7\Request('GET', $remoteUrl, ['http_errors' => false]);
            try {
                $promise = $client->sendAsync($request)->then(function ($response) use ($remoteUrl, $dir, $name) {
                    try {
                        $filePath = "{$dir}/{$name}";
                        \Storage::disk('local')->put($filePath, $response->getBody()->getContents());

                    } catch (\Exception $e) {
                        throw new \Exception($e->getMessage());
                    }
                });
                $promise->wait();
            } catch (\GuzzleHttp\Exception\BadResponseException $e) {
                throw new \Exception($e->getMessage());
            }
        } catch (\Exception $e) {
            throw new \Exception($e->getMessage());
        }
        return [
            'file_type' => $this->getFileTypeByExit($ext),
            'file_path' => $dir . DIRECTORY_SEPARATOR . $name,
            'origin_name' => 'remote.' . $ext,
            'new_name' => $name,
            'file_ext' => $ext,
        ];
    }

    /**
     * 下载远程文件地址到本地相对文件目录
     * @param $remoteUrl 远程文件访问地址
     * @param string $filePath 文件相对目录，非绝对文件目录
     * @return array
     * @throws \Exception
     */
    public function downloadRemoteFileByFilePath($remoteUrl, $filePath)
    {
        try {
            $client = new \GuzzleHttp\Client();
            $request = new \GuzzleHttp\Psr7\Request('GET', $remoteUrl, ['http_errors' => false]);
            try {
                $promise = $client->sendAsync($request)->then(function ($response) use ($remoteUrl, $filePath) {
                    try {
                        \Storage::disk('local')->put($filePath, $response->getBody()->getContents());
                    } catch (\Exception $e) {
                        throw new \Exception($e->getMessage());
                    }
                });
                $promise->wait();
            } catch (\GuzzleHttp\Exception\BadResponseException $e) {
                throw new \Exception($e->getMessage());
            }
        } catch (\Exception $e) {
            throw new \Exception($e->getMessage());
        }
    }
    /**
     * 获取远程地址在本地的文件地址
     * @param $remoteUrl
     * @return string
     */
    public function getLocalFileInfoByRemoteUrl($remoteUrl, $ext = 'jpg')
    {
        $findFile = SysFile::where('view_url', $remoteUrl)->first();
        if ($findFile) {//数据库中存在
            $fileExit = \Storage::disk('local')->exists($findFile['file_path']);
            //如果本地文件不存在则下载到本地
            if (!$fileExit) {
                $this->downloadRemoteFileByFilePath($findFile['view_url'], $findFile['file_path']);
            }
            $findFile['local_file_path'] = \Storage::disk('local')->path($findFile['file_path']);
            $fileData = $findFile->toArray();
            return $fileData;
        } else {//数据库中不存在
            //保存远程文件到本地
            $localFileInfo = $this->saveRemoteFile($remoteUrl, '', $ext);
            $fileInfo = [
                'disk_name' => 'sfy',
                'file_type' => $localFileInfo['file_type'],
                'file_ext' => $localFileInfo['file_ext'],
                'origin_name' => $localFileInfo['origin_name'],
                'new_name' => $localFileInfo['new_name'],
                'file_path' => $localFileInfo['file_path'],
            ];
            //获取文件绝对路径
            $fileRealPath = \Storage::disk('local')->path($fileInfo['file_path']);
            $uploadInfo = $this->uploadFile($fileRealPath);
            $fileInfo['view_url'] = $uploadInfo['url'];
            //文件大小
            $fileInfo['file_size'] = $uploadInfo['size'];
            //保存文件数据
            $saveFile = $this->saveFileInfo($fileInfo);
            $fileInfo['local_file_path'] = $fileRealPath;
            $fileInfo['file_id'] = $saveFile->file_id;
            return $fileInfo;
        }
    }
}